#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <assert.h>

#include "net_context.h"
#include "tcp_impl.h"

#define SYN_SIZE    1
#define FIN_SIZE    1
#define ACK_SIZE    0

using namespace usnet;

std::unique_ptr<TCPImpl> TCPImpl::MakeUnique(
    EndpointAddr* laddr,
    EndpointAddr* raddr,
    const int64_t isn,
    const int32_t rw_flags)
{
    auto ctx = NetContext::GetContext();
    if (ctx->GetPort(ntohs(laddr->be_port_)) ==  false) {
        LOG_ERROR(ctx, "port number is already in use");
        return nullptr;
    }
    LOG_DEBUG(ctx, "get port success");
    std::unique_ptr<TCPImpl> tcp_impl { new(std::nothrow) TCPImpl { laddr, raddr, isn, rw_flags } };
    if (tcp_impl == nullptr) {
        LOG_ERROR(ctx, "failed to new TCPImpl instance");
        return nullptr;
    }
    LOG_DEBUG(ctx, "new TCPImpl instance success");
    if (tcp_impl->InitIP4Impl() == false) {
        LOG_ERROR(ctx, "failed to init IP4Impl");
        return nullptr;
    }
    LOG_DEBUG(ctx, "init IP4Impl success");
    if (tcp_impl->PrefillHeader() == false) {
        LOG_ERROR(ctx, "TCPImpl::FillHeaders() failed");
        return nullptr;
    }
    LOG_DEBUG(ctx, "prefill TCP header success");
    if (tcp_impl->ShakeHands() ==  false) {
        LOG_ERROR(ctx, "TCPImpl::ShakeHands() failed");
        return nullptr;
    }
    LOG_DEBUG(ctx, "TCP shaking hands success");
    if ((tcp_impl->rw_flags_ & RW_SYNC) == 0) {
        tcp_impl->StartTransport();
        LOG_DEBUG(ctx, "start transportation success");
    }
    return tcp_impl;
}

int TCPImpl::SendData(
    const uint8_t flags,
    const char* data,
    const uint32_t len,
    const uint16_t urg)
{
    auto ctx = NetContext::GetContext();
    if (status_ != TCPStatus::ESTABLISHED) {
        LOG_ERROR(ctx, "TCP connection not established");
        return -1;
    }
    if (data == nullptr) {
        LOG_ERROR(ctx, "invalide source data address");
        return -1;
    }
    const char* src { data };
    uint32_t sent_len { 0 };
    uint32_t rest_len { len };
    FrameBuf* fb { nullptr };
    while ((fb = fb_mgr_->tx_fb_free_.GetHead()) && rest_len) {
        uint32_t max_payload_size = MAX_INET_PAYLOAD - GetHeaderSize();
        uint32_t payload_size = rest_len > max_payload_size ? max_payload_size : rest_len;
        FillSegment(fb, flags, src, payload_size, urg);
        src += payload_size;
        sent_len += payload_size;
        rest_len -= payload_size;
        fb_mgr_->tx_fb_free_.RemoveHead();
        tx_fb_pend_.TailInsert(fb);
    }

    if (rw_flags_ & RW_SYNC) {
        uint32_t rx_data_total = DoRecv();
        DoSend(rx_data_total);
        if (status_ == TCPStatus::CLOSE_WAIT) {
            // pass
        }
    }
    return sent_len;
}

int TCPImpl::SendData(
        const uint8_t flags,
        const struct iovec* iov,
        const uint32_t iov_len,
        const uint16_t urg)
{
    auto ctx = NetContext::GetContext();
    if (status_ != TCPStatus::ESTABLISHED) {
        LOG_ERROR(ctx, "TCP connection not established");
        return -1;
    }
    if (iov == nullptr) {
        LOG_ERROR(ctx, "invalide source data address");
        return -1;
    }
    uint32_t sent_len { 0 };
    for (int k = 0; k < iov_len; ++k) {
        const char* data = (const char*) iov[k].iov_base;
        const uint32_t len = iov[k].iov_len;
        int ret = SendData(flags, data, len, urg);
        if (ret <= 0) {
            break;
        }
        sent_len += ret;
    }
    
    return sent_len;
}

int TCPImpl::RecvData(char* buf, const uint32_t len)
{
    auto ctx = NetContext::GetContext();
    if (status_ != TCPStatus::ESTABLISHED) {
        LOG_ERROR(ctx, "TCP connection not established");
        return -1;
    }
    if (buf == nullptr) {
        LOG_ERROR(ctx, "invalid destination data buffer address");
        return -1;
    }
    if (rw_flags_ & RW_SYNC) {
        uint32_t rx_data_total = DoRecv();
        DoSend(rx_data_total);
        if (status_ == TCPStatus::CLOSE_WAIT) {
            // pass
        }
    }
    char* dest { buf };
    uint32_t recv_len { 0 };
    uint32_t rest_len { len };
    FrameBuf* fb { nullptr };
    while ((fb = rx_fb_pend_.GetHead()) && rest_len) {
        uint32_t payload_size = GetDataSize(fb);
        if (payload_size > rest_len) {
            break;
        }
        memcpy(dest, (char*)fb->tran_hdr_.tcp_hdr_ + GetHeaderSize(), payload_size);
        dest += payload_size;
        recv_len += payload_size;
        rest_len -= payload_size;
        rx_fb_pend_.RemoveHead();
        if (fb_mgr_->RecvInit(fb)) {
            fb_mgr_->rx_fb_free_.TailInsert(fb);
        }
    }
    fb_mgr_->RecvPush();
    return recv_len;
}

int TCPImpl::RecvData(struct iovec* iov, const uint32_t iov_len)
{
    auto ctx = NetContext::GetContext();
    if (status_ != TCPStatus::ESTABLISHED) {
        LOG_ERROR(ctx, "TCP connection not established");
        return -1;
    }
    if (iov == nullptr) {
        LOG_ERROR(ctx, "invalid destination data buffer address");
        return -1;
    }
    uint32_t recv_len { 0 };
    for (int k = 0; k < iov_len; ++k) {
        char* buf = (char*) iov[k].iov_base;
        const uint32_t len = iov[k].iov_len;
        int ret = RecvData(buf, len);
        if (ret <= 0) {
            break;
        }
        recv_len += ret;
    }
    return recv_len;
}

uint32_t TCPImpl::RecvData(const struct iovec** iov, const uint32_t size)
{
    // pass
    return 0;
}

uint32_t TCPImpl::Reserve(const struct iovec** iov, const uint32_t size)
{
    // pass
    return 0;
}

void TCPImpl::Discard(const struct iovec* iov)
{
    // pass
}

bool TCPImpl::Commit(const struct iovec* iov)
{
    // pass
    return false;
}

bool TCPImpl::PrefillHeader()
{
    auto ctx = NetContext::GetContext();
    auto fb_mgr = ip_impl_->PrefillHeader();
    FrameBuf* fb { nullptr };
    LinkNode list_head { std::move(fb_mgr->tx_fb_free_) };
    while ((fb = list_head.GetHead())) {
        TCPHeader* tcp_hdr { nullptr };
        if (laddr_->inet_proto_ == InetProto::IPv4) {
            tcp_hdr = (TCPHeader*) ((char*)fb->inet_hdr_.ip4_hdr_ + ip_impl_->GetHeaderSize());
        }
        if (laddr_->inet_proto_ == InetProto::IPv6) {
            tcp_hdr = (TCPHeader*) ((char*)fb->inet_hdr_.ip6_hdr_ + ip_impl_->GetHeaderSize());
        }
        tcp_hdr->be_lport_ = laddr_->be_port_;
        tcp_hdr->be_rport_ = raddr_->be_port_;

        tcp_hdr->hdr_len_  = (hdr_size_ << 2);
        tcp_hdr->be_rwnd_size_ = htons(static_cast<uint16_t>(-1u));
        tcp_hdr->be_urg_ptr_ = 0;

        fb->tran_hdr_.tcp_hdr_ = tcp_hdr;
        list_head.RemoveHead();
        fb_mgr->tx_fb_free_.TailInsert(fb);
    }

    list_head = std::move(fb_mgr->rx_fb_free_);
    while ((fb = list_head.GetHead())) {
        TCPHeader* tcp_hdr { nullptr };
        if (laddr_->inet_proto_ == InetProto::IPv4) {
            tcp_hdr = (TCPHeader*) ((char*)fb->inet_hdr_.ip4_hdr_ + ip_impl_->GetHeaderSize());
        }
        if (laddr_->inet_proto_ == InetProto::IPv6) {
            tcp_hdr = (TCPHeader*) ((char*)fb->inet_hdr_.ip6_hdr_ + ip_impl_->GetHeaderSize());
        }
        fb->tran_hdr_.tcp_hdr_ = tcp_hdr;
        list_head.RemoveHead();
        fb_mgr->rx_fb_free_.TailInsert(fb);
    }

    fb_mgr_ = fb_mgr;
    return true;
}

bool TCPImpl::ShakeHands()
{
    auto ctx = NetContext::GetContext();
    if (status_ == TCPStatus::CLOSED) {
        if (SendSyn() == false) {
            LOG_ERROR(ctx, "failed to send syn");
            return false;
        }
        LOG_DEBUG(ctx, "sending SYN success");
        if (RecvSynAck() == false) {
            LOG_ERROR(ctx, "failed to wait for ack of syn");
            return false;
        }
        LOG_DEBUG(ctx, "receiving SYN&ACK success");
        if (SendAck() == false) {
            LOG_ERROR(ctx, "failed to send syn ack");
            return false;
        }
        LOG_DEBUG(ctx, "sending ACK success");
        return true;
    }
    if (status_ == TCPStatus::LISTENING) {
        if (SendSynAck() == false) {
            LOG_ERROR(ctx, "failed to send syn ack");
            return false;
        }
        LOG_DEBUG(ctx, "sending SYN|ACK success");
        if (RecvAck() ==  false) {
            LOG_ERROR(ctx, "failed to receive ack");
            return false;
        }
        LOG_DEBUG(ctx, "receiving ACK success");
        return true;
    }
    return false;
}

bool TCPImpl::FareWell()
{
    auto ctx = NetContext::GetContext();
    if (status_ == TCPStatus::ESTABLISHED) {
        if (SendFin() == false) {
            LOG_ERROR(ctx, "failed to send FIN");
            return false;
        }
        LOG_DEBUG(ctx, "sending FIN success");
        if (RecvAck() == false) {
            LOG_ERROR(ctx, "failed to receive ACK");
            return false;
        }
        LOG_DEBUG(ctx, "receiving ACK success");
        if (RecvFin() == false) {
            LOG_ERROR(ctx, "failed to receive FIN");
            return false;
        }
        LOG_DEBUG(ctx, "receiving FIN success");
        if (SendAck() ==  false) {
            LOG_ERROR(ctx, "failed to send ACK");
            return false;
        }
        LOG_DEBUG(ctx, "sending ACK success");
        return true;
    }
    if (status_ ==TCPStatus::CLOSE_WAIT) {
        if (SendAck() == false) {
            LOG_ERROR(ctx, "failed to send ACK");
            return false;
        }
        LOG_DEBUG(ctx, "sending ACK success");
        if (SendFin() == false) {
            LOG_ERROR(ctx, "failed to send FIN");
            return false;
        }
        LOG_DEBUG(ctx, "sending FIN success");
        if (RecvAck() == false) {
            LOG_ERROR(ctx, "failed to receive ACK");
            return false;
        }
        LOG_DEBUG(ctx, "receiving ACK success");
        return true;
    }
    LOG_ERROR(ctx, "bad TCP status");
    return false;
}

bool TCPImpl::SendSyn()
{
    auto ctx = NetContext::GetContext();
    // check TCP status
    assert(status_ == TCPStatus::CLOSED);
    // get and check buffer
    auto fb = fb_mgr_->tx_fb_free_.GetHead();
    assert(fb);
    // fill segment
    FillSegment(fb, SYN_BIT);
    // update sending buffer status
    fb_mgr_->tx_fb_free_.RemoveHead();
    assert(fb_mgr_->tx_fb_fill_.GetHead() == nullptr);
    fb_mgr_->tx_fb_fill_.TailInsert(fb);

    /**********************************************************************
     ******** start transportation
     * note: sending normally ends here. but transportation service has not 
     * been started yet, hence we have to do transportation work by ourselves.
     */
    /**********************************************************************
     * fill and update sequence, fowlowing are the rules: 
     * tcp_hdr->be_syn_seq_ = htonl(tx_syn_seq_);
     * tcp_hdr->be_ack_seq_ = htonl(rx_syn_seq_);
     * tx_syn_seq_ += payload_size;
     * rx_ack_seq_ = tcp_hdr->be_ack_seq_;
     */
    auto tcp_hdr = fb->tran_hdr_.tcp_hdr_;
    tcp_hdr->be_syn_seq_ = htonl(tx_syn_seq_);
    tcp_hdr->be_ack_seq_ = htonl(rx_syn_seq_);
    tx_syn_seq_ += SYN_SIZE;
    rx_ack_seq_ = ntohl(tcp_hdr->be_ack_seq_);
    tcp_hdr->be_checksum_ = 0;
    tcp_hdr->be_checksum_ = htons(CheckSum(fb));
    /**********************************************************************/
    // passing sending request to IPImpl
    ip_impl_->SendPacket();
    /**********************************************************************/
    // update TCP status
    status_ = TCPStatus::SYN_SENT;
    return true;
}

bool TCPImpl::SendSynAck()
{
    auto ctx = NetContext::GetContext();
    // check TCP status
    assert(status_ == TCPStatus::LISTENING);
    // get and check buffer
    auto fb = fb_mgr_->tx_fb_free_.GetHead();
    assert(fb);
    // fill segment
    FillSegment(fb, SYN_BIT | ACK_BIT);
    // update sending buffer status
    fb_mgr_->tx_fb_free_.RemoveHead();
    assert(fb_mgr_->tx_fb_fill_.GetHead() == nullptr);
    fb_mgr_->tx_fb_fill_.TailInsert(fb);

    /**********************************************************************
     ******** start transportation
     * note: sending normally ends here. but transportation service has not 
     * been started yet, hence we have to do transportation work by ourselves.
     */
    /**********************************************************************
     * fill and update sequence, fowlowing are the rules: 
     * tcp_hdr->be_syn_seq_ = htonl(tx_syn_seq_);
     * tcp_hdr->be_ack_seq_ = htonl(rx_syn_seq_);
     * tx_syn_seq_ += payload_size;
     * rx_ack_seq_ = tcp_hdr->be_ack_seq_;
     */
    auto tcp_hdr = fb->tran_hdr_.tcp_hdr_;
    tcp_hdr->be_syn_seq_ = htonl(tx_syn_seq_);
    tcp_hdr->be_ack_seq_ = htonl(rx_syn_seq_);
    tx_syn_seq_ += SYN_SIZE;
    rx_ack_seq_ = ntohl(tcp_hdr->be_ack_seq_);
    tcp_hdr->be_checksum_ = 0;
    tcp_hdr->be_checksum_ = htons(CheckSum(fb));
    /**********************************************************************/
    // passing sending request to IPImpl
    ip_impl_->SendPacket();
    /**********************************************************************/
    // update TCP status
    status_ = TCPStatus::SYN_RECV;
    return true;
}

bool TCPImpl::RecvSynAck()
{
    auto ctx = NetContext::GetContext();
    // check TCP status
    assert(status_ == TCPStatus::SYN_SENT);
    /**********************************************************************
     ******** start transportation
     * note: since transportation service has not been started, we have to do
     * the transportation work by ourselves before receiving data.
     */
    // passing receiving request to IPimpl
    // get and check buffer
    uint32_t retry_count { 200 };
    while (retry_count--) {
        if (ip_impl_->RecvPacket()) {
            break;
        }
        // LOG_DEBUG(ctx, "retry receiving SYN|ACK: count = %u", (200 - retry_count));
        usleep(5000);
    }
    auto fb = fb_mgr_->rx_fb_recv_.GetHead();
    if (fb == nullptr) {
        LOG_ERROR(ctx, "TCP receiving buffer is empty");
        return false;
    }

    // assert(fb && (fb->tsp_pdu_ == GetHeaderSize()));
    // verify checksum
    // if (CheckSum(fb) != 0) {
    //     LOG_ERROR(ctx, "failed to verify segment checksum");
    //     return false;
    // }
    // handle segment flags
    auto tcp_hdr = fb->tran_hdr_.tcp_hdr_;
    if ((tcp_hdr->tcp_flags_ & SYN_BIT) == 0) {
        LOG_ERROR(ctx, "SYN bit is not set");
        return false;
    }
    if (rx_syn_seq_ != tcp_hdr->be_syn_seq_) {
        /* normally this means receving segment(s) missed, thus should return error.
         * but this is the first receving, hence we will simply reset rx_syn_seq_ to
         * tcp_hdr->be_syn_seq_
         */
        rx_syn_seq_ = ntohl(tcp_hdr->be_syn_seq_);
    }
    rx_syn_seq_ += SYN_SIZE;
    if ((tcp_hdr->tcp_flags_ & ACK_BIT) == 0){
        LOG_ERROR(ctx, "ACK bit is not set");
        return false;
    }
    if (tx_syn_seq_ != ntohl(tcp_hdr->be_ack_seq_)) {
        LOG_ERROR(ctx, "incorrect ack sequence: tx_syn_seq = %u, ", tx_syn_seq_);
        return false;
    }
    tx_ack_seq_ = ntohl(tcp_hdr->be_ack_seq_);
    /**********************************************************************/
    // update sending buffer status
    fb = fb_mgr_->tx_fb_sent_.GetHead();
    assert(fb);
    fb_mgr_->tx_fb_sent_.RemoveHead();
    fb_mgr_->tx_fb_free_.TailInsert(fb);
    /**********************************************************************/

    // normally we should copy payload to user buffer here. pass since there is no
    // payload in this case.

    // update receiving buffer status
    fb = fb_mgr_->rx_fb_recv_.GetHead();
    fb_mgr_->rx_fb_recv_.RemoveHead();
    if (fb_mgr_->RecvInit(fb)) {
        fb_mgr_->rx_fb_free_.TailInsert(fb);
    }
    fb_mgr_->RecvPush();

    return true;
}

bool TCPImpl::SendAck()
{
    // check TCP status
    assert(status_ == TCPStatus::SYN_SENT);
    // get and check buffer
    auto fb = fb_mgr_->tx_fb_free_.GetHead();
    assert(fb);
    // fill segment
    FillSegment(fb, ACK_BIT);
    // update sending buffer status
    fb_mgr_->tx_fb_free_.RemoveHead();
    // assert(fb_mgr_->tx_fb_fill_.GetHead() == nullptr);
    fb_mgr_->tx_fb_fill_.TailInsert(fb);

    /**********************************************************************
     ******** start transportation
     * note: sending normally ends here. but transportation service has not 
     * been started yet, hence we have to do transportation work by ourselves.
     */
    /**********************************************************************
     * fill and update sequence, fowlowing are the rules: 
     * tcp_hdr->be_syn_seq_ = htonl(tx_syn_seq_);
     * tcp_hdr->be_ack_seq_ = htonl(rx_syn_seq_);
     * tx_syn_seq_ += payload_size;
     * rx_ack_seq_ = tcp_hdr->be_ack_seq_;
     */
    auto tcp_hdr = fb->tran_hdr_.tcp_hdr_;
    tcp_hdr->be_syn_seq_ = htonl(tx_syn_seq_);
    tcp_hdr->be_ack_seq_ = htonl(rx_syn_seq_);
    tx_syn_seq_ += ACK_SIZE;
    rx_ack_seq_ = ntohl(tcp_hdr->be_ack_seq_);
    /**********************************************************************/
    // note that this is an extra step. this make the buffer reposted right after
    // comsumed.
    fb->flags_ = FB_REUSE;
    // passing sending request to IPImpl
    ip_impl_->SendPacket();
    /**********************************************************************/
    // update TCP status
    status_ = TCPStatus::ESTABLISHED;
    return true;
}

bool TCPImpl::RecvAck()
{
    auto ctx = NetContext::GetContext();
    /**********************************************************************
     ******** start transportation
    * note: since transportation service has not been started, we have to do
    * the transportation work by ourselves before receiving data.
    */
    // passing receiving request to IPimpl
    uint32_t retry_count { 2000 };
    while (retry_count--) {
        if (ip_impl_->RecvPacket()) {
            break;
        }
        // LOG_DEBUG(ctx, "retry receiving ACK: count = %u", (2000 - retry_count));
        usleep(50);
    }
    // get and check buffer
    auto fb = fb_mgr_->rx_fb_recv_.GetHead();
    if (fb == nullptr) {
        LOG_ERROR(ctx, "TCP receiving buffer is empty");
        return false;
    }
    // assert(fb && (fb->tsp_pdu_ == 0));
    // verify checksum
    // if (CheckSum(fb) != 0) {
    //     LOG_ERROR(ctx, "failed to verify segment checksum");
    //     return false;
    // }
    // handle segment flags
    auto tcp_hdr = fb->tran_hdr_.tcp_hdr_;
    if ((tcp_hdr->tcp_flags_ & ACK_BIT) == 0){
        LOG_ERROR(ctx, "ACK bit is not set");
        return false;
    }
    if (tx_syn_seq_ != ntohl(tcp_hdr->be_ack_seq_)) {
        LOG_ERROR(ctx, "incorrect ack sequence");
        return false;
    }
    tx_ack_seq_ = ntohl(tcp_hdr->be_ack_seq_);
    /**********************************************************************/
    // update sending buffer status
    fb = fb_mgr_->tx_fb_sent_.GetHead();
    assert(fb);
    fb_mgr_->tx_fb_sent_.RemoveHead();
    fb_mgr_->tx_fb_free_.TailInsert(fb);
    /**********************************************************************/

    // normally we should copy payload to user buffer here. pass since there is no
    // payload in this case.

    // update receiving buffer status
    fb = fb_mgr_->rx_fb_recv_.GetHead();
    fb_mgr_->rx_fb_recv_.RemoveHead();
    if (fb_mgr_->RecvInit(fb)) {
        fb_mgr_->rx_fb_free_.TailInsert(fb);
    }
    fb_mgr_->RecvPush();

    if (status_ == TCPStatus::SYN_RECV){
        status_ = TCPStatus::ESTABLISHED;
    }
    if (status_ == TCPStatus::FIN_WAIT1) {
        status_ = TCPStatus::FIN_WAIT2;
    }
    if (status_ == TCPStatus::LAST_ACK) {
        status_ = TCPStatus::CLOSED;
    }

    return true;
}

bool TCPImpl::SendFin()
{
    auto ctx = NetContext::GetContext();
    // get and check buffer
    auto fb = fb_mgr_->tx_fb_free_.GetHead();
    assert(fb);
    // fill segment
    FillSegment(fb, FIN_BIT);
    // update sending buffer status
    fb_mgr_->tx_fb_free_.RemoveHead();
    assert(fb_mgr_->tx_fb_fill_.GetHead() == nullptr);
    fb_mgr_->tx_fb_fill_.TailInsert(fb);

    /**********************************************************************
     ******** start transportation
     * note: sending normally ends here. but transportation service has not 
     * been started yet, hence we have to do transportation work by ourselves.
     */
    /**********************************************************************
     * fill and update sequence, fowlowing are the rules: 
     * tcp_hdr->be_syn_seq_ = htonl(tx_syn_seq_);
     * tcp_hdr->be_ack_seq_ = htonl(rx_syn_seq_);
     * tx_syn_seq_ += payload_size;
     * rx_ack_seq_ = tcp_hdr->be_ack_seq_;
     */
    auto tcp_hdr = fb->tran_hdr_.tcp_hdr_;
    tcp_hdr->be_syn_seq_ = htonl(tx_syn_seq_);
    tcp_hdr->be_ack_seq_ = htonl(rx_syn_seq_);
    tx_syn_seq_ += FIN_SIZE;
    rx_ack_seq_ = ntohl(tcp_hdr->be_ack_seq_);
    tcp_hdr->be_checksum_ = 0;
    tcp_hdr->be_checksum_ = htons(CheckSum(fb));
    /**********************************************************************/
    // passing sending request to IPImpl
    ip_impl_->SendPacket();
    /**********************************************************************/
    // update TCP status
    if (status_ == TCPStatus::ESTABLISHED) {
        status_ = TCPStatus::FIN_WAIT1;
    }
    if (status_ == TCPStatus::CLOSE_WAIT) {
        status_ = TCPStatus::LAST_ACK;
    }
    return true;
}

bool TCPImpl::RecvFin()
{
    auto ctx = NetContext::GetContext();
    assert(status_ == TCPStatus::FIN_WAIT2);
    /**********************************************************************
     ******** start transportation
    * note: since transportation service has not been started, we have to do
    * the transportation work by ourselves before receiving data.
    */
    // passing receiving request to IPimpl
    ip_impl_->RecvPacket();
    // get and check buffer
    auto fb = fb_mgr_->rx_fb_recv_.GetHead();
    if (fb == nullptr) {
        LOG_ERROR(ctx, "TCP receiving buffer is empty");
        return false;
    }
    // assert(fb && (fb->tsp_pdu_ == 0));
    // verify checksum
    if (CheckSum(fb) != 0) {
        LOG_ERROR(ctx, "failed to verify segment checksum");
        return false;
    }
    rx_syn_seq_ += FIN_SIZE;
    // handle segment flags
    auto tcp_hdr = fb->tran_hdr_.tcp_hdr_;
    if ((tcp_hdr->tcp_flags_ & FIN_BIT) == 0){
        LOG_ERROR(ctx, "ACK bit is not set");
        return false;
    }
    if (tx_syn_seq_ = ntohl(tcp_hdr->be_ack_seq_)) {
        LOG_ERROR(ctx, "incorrect ack sequence");
        return false;
    }
    tx_ack_seq_ = ntohl(tcp_hdr->be_ack_seq_);
    /**********************************************************************/
    // update sending buffer status
    fb = fb_mgr_->tx_fb_sent_.GetHead();
    fb_mgr_->tx_fb_sent_.RemoveHead();
    fb_mgr_->tx_fb_free_.TailInsert(fb);
    /**********************************************************************/

    // normally we should copy payload to user buffer here. pass since there is no
    // payload in this case.

    // update receiving buffer status
    fb = fb_mgr_->rx_fb_recv_.GetHead();
    fb_mgr_->rx_fb_recv_.RemoveHead();
    if (fb_mgr_->RecvInit(fb)) {
        fb_mgr_->rx_fb_free_.TailInsert(fb);
    }
    fb_mgr_->RecvPush();
    status_ = TCPStatus::TIME_WAIT;
    // to do: timing
    status_ = TCPStatus::CLOSED;

    return true;
}

void TCPImpl::FillSegment(
    FrameBuf* fb,
    const uint8_t flags,
    const char* data,
    const uint32_t len,
    const uint16_t urg)
{
    auto ctx = NetContext::GetContext();
    auto tcp_hdr = fb->tran_hdr_.tcp_hdr_;
    assert(tcp_hdr);
    if (data && len) {
        memcpy((char*)tcp_hdr + GetHeaderSize(), data, len);
    }
    fb->tsp_pdu_ = GetHeaderSize() + len;
    tcp_hdr->hdr_len_    = (hdr_size_ << 4);
    tcp_hdr->tcp_flags_  = flags;
    tcp_hdr->be_rwnd_size_ = htons(fb_mgr_->GetRecvWnd());
    tcp_hdr->be_urg_ptr_ = htons(urg);
}

uint16_t TCPImpl::CheckSum(const FrameBuf* fb)
{
    auto tcp_hdr = fb->tran_hdr_.tcp_hdr_;
    uint16_t* buf { nullptr };
    uint16_t  len { 0 };
    uint64_t  csum1 { 0 };
    if (laddr_->inet_proto_ == InetProto::IPv4) {
        len = ntohs(fb->inet_hdr_.ip4_hdr_->be_len_ - htons(static_cast<uint16_t>(ip_impl_->GetHeaderSize())));
        IP4PseudoHdr ip4_pseudo_hdr {
            laddr_->be_ip_.v4_, raddr_->be_ip_.v4_,
            static_cast<uint8_t>(PPT::TCP), htons(len) };
        buf = (uint16_t*)(&ip4_pseudo_hdr);
        csum1 = InetCheckSum(buf, (sizeof(IP4PseudoHdr) >> 1));
    }
    if (laddr_->inet_proto_ == InetProto::IPv6) {
        // pass
    }
    buf = (uint16_t*)tcp_hdr;
    uint64_t csum2 = InetCheckSum(buf, (len >> 1));
    uint16_t remainder { 0 };
    if (len & 1u) {
        remainder = *((uint8_t*)buf + len - 1);
        remainder <<= 8;
        remainder = ntohs(remainder);
    }
    csum1 += static_cast<uint64_t>(remainder);
    csum1 += csum2;
    while ((csum2 = (csum1 >> 16))) {
        csum1 &= 0xffff;
        csum1 += csum2;
    }
    return ~static_cast<uint16_t>(csum1);
}

uint32_t TCPImpl::DoRecv()
{
    auto ctx = NetContext::GetContext();
    /*****************************************************
     * receive segments
    */
    uint32_t rx_data_total { 0 };
    if (ip_impl_->RecvPacket()) {
        LOG_DEBUG(ctx, "new IP packet(s) received");
        // sort buffer by TCP syn sequence, is this necessary?
        SortSegment(fb_mgr_->rx_fb_recv_);
        // get and check buffer
        FrameBuf* fb { nullptr };
        while ((fb = fb_mgr_->rx_fb_recv_.GetHead())) {
            LOG_DEBUG(
                ctx,
                "TCP segment received, rx_syn_seq_ = %u, tx_ack_seq_ = %u",
                rx_syn_seq_,
                tx_ack_seq_);
            auto fp = ctx->GetDumpFP();
            if (fp) {
                fb->eth_hdr_->Dump(fp);
                fb->inet_hdr_.ip4_hdr_->Dump(fp);
                fb->tran_hdr_.tcp_hdr_->Dump(fp);
            }

            assert(CheckSum(fb) == 0);
            HandleTCPFlags(fb->tran_hdr_.tcp_hdr_);
            rx_data_total += GetDataSize(fb);
            
            // update receiving buffer status
            fb_mgr_->rx_fb_recv_.RemoveHead();
            rx_fb_pend_.TailInsert(fb);
        }
    }
    rx_syn_seq_ += rx_data_total;
    /*****************************************************/

    /*****************************************************
     * free acknowledged sent buffers
    */
    FrameBuf* fb { nullptr };
    while ((fb = fb_mgr_->tx_fb_sent_.GetHead())) {
        // we assume fb_mgr_->tx_fb_sent_ is sorted here, but is it?
        if (ntohl(fb->tran_hdr_.tcp_hdr_->be_syn_seq_) >= tx_ack_seq_) {
            break;
        }
        fb_mgr_->tx_fb_sent_.RemoveHead();
        fb_mgr_->tx_fb_free_.TailInsert(fb);
    }
    /*****************************************************/
    return rx_data_total;
}

void TCPImpl::DoSend(const uint32_t rx_data_total)
{
    auto ctx = NetContext::GetContext();
    FrameBuf* fb { nullptr };
    fb = tx_fb_pend_.GetHead();
    if ((fb == nullptr) && (rx_data_total == 0)) {
        return;
    }
    // ack of received segments to send
    if (fb == nullptr) {
        auto free_fb = fb_mgr_->tx_fb_free_.GetHead();
        assert(free_fb);
        FillSegment(free_fb, ACK_BIT);
        fb_mgr_->tx_fb_free_.RemoveHead();
        tx_fb_pend_.TailInsert(free_fb);
        fb = tx_fb_pend_.GetHead();
    }
    while ((fb = tx_fb_pend_.GetHead())) {
        auto tcp_hdr = fb->tran_hdr_.tcp_hdr_;
        tcp_hdr->be_syn_seq_  = htonl(tx_syn_seq_);
        tcp_hdr->be_ack_seq_  = htonl(rx_syn_seq_);
        rx_ack_seq_           = rx_syn_seq_;
        tx_syn_seq_          += GetDataSize(fb);
        tcp_hdr->be_checksum_ = 0;
        tcp_hdr->be_checksum_ = htons(CheckSum(fb));
        assert(tcp_hdr->be_checksum_);
        tx_fb_pend_.RemoveHead();
        fb_mgr_->tx_fb_fill_.TailInsert(fb);
    }
    // passing send resquest to IP
    ip_impl_->SendPacket();
}

void TCPImpl::HandleTCPFlags(const TCPHeader* tcp_hdr)
{
    auto ctx = NetContext::GetContext();
    // if (tcp_hdr->tcp_flags_ & RST_BIT) {
    //     status_ = TCPStatus::CLOSED;
    //     LOG_DEBUG(ctx, "TCP connection reset by remote end");
    //     return LoopCtrl::RETURN;
    // }
    // if (tcp_hdr->tcp_flags_ & FIN_BIT) {
    //     status_ == TCPStatus::CLOSE_WAIT;
    //     return LoopCtrl::BREAK;
    // }
    if (tcp_hdr->tcp_flags_ & ACK_BIT) {
        assert(tx_syn_seq_ >= ntohl(tcp_hdr->be_ack_seq_));
        tx_ack_seq_ = ntohl(tcp_hdr->be_ack_seq_);
    }
    // if (tcp_hdr->tcp_flags_ & SYN_BIT) {
    //     // pass
    // }
    // if (tcp_hdr->tcp_flags_ & PSH_BIT) {
    //     // pass
    // }
    // if (tcp_hdr->tcp_flags_ & URG_BIT) {
    //     // pass
    // }
    // if (tcp_hdr->tcp_flags_ & ECE_BIT) {
    //     // pass
    // }
    // if (tcp_hdr->tcp_flags_ & RWR_BIT) {
    //     // pass
    // }
}

void TCPImpl::SortSegment(LinkNode& list_head)
{
    // pass
    return;
}